//package com.xx.eurekaclient;
//
//
//import com.alibaba.fastjson.JSONObject;
//import lombok.extern.slf4j.Slf4j;
//import org.apache.commons.lang.StringUtils;
//import org.reactivestreams.Publisher;
//import org.springframework.beans.factory.annotation.Autowired;
//import org.springframework.cloud.gateway.filter.GatewayFilterChain;
//import org.springframework.cloud.gateway.filter.GlobalFilter;
//import org.springframework.cloud.gateway.support.BodyInserterContext;
//import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
//import org.springframework.context.annotation.ComponentScan;
//import org.springframework.context.annotation.Configuration;
//import org.springframework.core.Ordered;
//import org.springframework.core.io.buffer.DataBuffer;
//import org.springframework.http.HttpHeaders;
//import org.springframework.http.HttpMethod;
//import org.springframework.http.MediaType;
//import org.springframework.http.server.reactive.ServerHttpRequest;
//import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
//import org.springframework.http.server.reactive.ServerHttpResponse;
//import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
//import org.springframework.util.MultiValueMap;
//import org.springframework.web.reactive.function.BodyInserter;
//import org.springframework.web.reactive.function.BodyInserters;
//import org.springframework.web.reactive.function.client.ClientResponse;
//import org.springframework.web.reactive.function.server.HandlerStrategies;
//import org.springframework.web.reactive.function.server.ServerRequest;
//import org.springframework.web.server.ServerWebExchange;
//import reactor.core.publisher.Flux;
//import reactor.core.publisher.Mono;
//
//import java.util.List;
//import java.util.Map;
//import java.util.stream.Stream;
//
///**
// * <p>
// *      请求url权限校验
// *      1.首先网关检查token是否有效，无效直接返回401，不调用签权服务
// *      2.调用签权服务器看是否对该请求有权限，有权限进入下一个filter，没有权限返回401
// * </p>
// *
// * @author Bertram
// * @since 11/5/2019 2:30 PM
// */
//@Configuration
//@ComponentScan(basePackages = "com.xx.eurekaclient")
//public class AccessGatewayFilter implements GlobalFilter ,Ordered{
//    //private String X_CLIENT_TOKEN_USER = "x-client-token-user";
//    //private final static String X_CLIENT_TOKEN = "x-client-token";
//    //private static final String BEARER = "Bearer";
//    //private Logger logger = LoggerFactory.getLogger(AccessGatewayFilter.class);
//    //
//    //
//    //
//    ////@Bean
//    ////public HiddenHttpMethodFilter hiddenHttpMethodFilter() {
//    ////    return new HiddenHttpMethodFilter() {
//    ////        @Override
//    ////        public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
//    ////            return chain.filter(exchange);
//    ////        }
//    ////    };
//    ////}
//    //
//    //@Override
//    //public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//    //    ServerHttpRequest serverHttpRequest = exchange.getRequest();
//    //
//    //    ServerHttpResponse serverHttpResponse = exchange.getResponse();
//    //    URI uri = serverHttpRequest.getURI();
//    //    HttpMethod method = serverHttpRequest.getMethod();
//    //    String methodValue = serverHttpRequest.getMethodValue();
//    //    String path = serverHttpRequest.getPath().value();
//    //    serverHttpRequest.getBody()
//    //    String contentType = serverHttpRequest.getHeaders().getFirst("Content-Type");
//    //    if ("POST".equals(methodValue) && !contentType.startsWith("multipart/form-data")){
//    //    // 获取body体
//    //    Object object = exchange.getAttribute("cachedRequestBodyObject");
//    //    // todo 可以在这里对请求做各种权限验证，并且修改body
//    //    String bodyStr = JSONObject.toJSONString(object);
//    //    //重新 封装request，传给下一级，由于post的body只能订阅一次，所以要再次封装请求到request   才行，不然会报错请求已经订阅过
//    //    ServerHttpRequest request = serverHttpRequest.mutate().uri(uri).build();
//    //    DataBuffer bodyDataBuffer = stringBuffer(JSONObject.toJSONString(object));
//    //    Flux<DataBuffer> bodyFlux = Flux.just(bodyDataBuffer);
//    //    request = new ServerHttpRequestDecorator(request) {
//    //        @Override
//    //        public Flux<DataBuffer> getBody() {
//    //            return bodyFlux;
//    //        }
//    //    };
//    //    // 特别注意，如果上面body长度经过修改，这边需要重新计算长度，以免下游服务获取body不完整
//    //    request.mutate().header(HttpHeaders.CONTENT_LENGTH, Integer.toString(bodyStr.length()));
//    //    return chain.filter(exchange.mutate().request(request).build());
//    //} else if (method.equals(HttpMethod.GET)) {
//    //    return chain.filter(exchange);
//    //}
//    //    return chain.filter(exchange);
//    //
//    //}
//    //
//    //
//    //
//    //private Mono<Void> unauthorized(ServerWebExchange serverWebExchange) {
//    //    serverWebExchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
//    //    DataBuffer buffer = serverWebExchange.getResponse()
//    //            .bufferFactory().wrap(HttpStatus.UNAUTHORIZED.getReasonPhrase().getBytes());
//    //    return serverWebExchange.getResponse().writeWith(Flux.just(buffer));
//    //}
//    //
//    //private Mono<Void> returnMono(GatewayFilterChain chain,ServerWebExchange exchange, AccessRecord accessRecord){
//    //    return chain.filter(exchange).then(Mono.fromRunnable(()->{
//    //        Long startTime = exchange.getAttribute("startTime");
//    //        if (startTime != null){
//    //            long executeTime = (System.currentTimeMillis() - startTime);
//    //            accessRecord.setExpendTime(executeTime);
//    //            accessRecord.setHttpCode(Objects.requireNonNull(exchange.getResponse().getStatusCode()).value());
//    //            writeAccessLog(JSONObject.toJSONString(accessRecord) + "\r\n");
//    //        }
//    //    }));
//    //}
//    //
//    ////@Override
//    ////public int getOrder() {
//    ////    return 1;
//    ////}
//    //
//    ///**
//    // * 获取请求体中的字符串内容
//    // * @param serverHttpRequest
//    // * @return
//    // */
//    //private String resolveBodyFromRequest(ServerHttpRequest serverHttpRequest){
//    //    //获取请求体
//    //    Flux<DataBuffer> body = serverHttpRequest.getBody();
//    //    StringBuilder sb = new StringBuilder();
//    //
//    //    body.subscribe(buffer -> {
//    //        byte[] bytes = new byte[buffer.readableByteCount()];
//    //        buffer.read(bytes);
//    //        DataBufferUtils.release(buffer);
//    //        String bodyString = new String(bytes, StandardCharsets.UTF_8);
//    //        sb.append(bodyString);
//    //    });
//    //    return sb.toString();
//    //
//    //}
//    //
//    ///**
//    // * 去掉空格,换行和制表符
//    // * @param str
//    // * @return
//    // */
//    //private String formatStr(String str){
//    //    if (str != null && str.length() > 0) {
//    //        Pattern p = Pattern.compile("\\s*|\t|\r|\n");
//    //        Matcher m = p.matcher(str);
//    //        return m.replaceAll("");
//    //    }
//    //    return str;
//    //}
//    //
//    //private DataBuffer stringBuffer(String value){
//    //    byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
//    //    NettyDataBufferFactory nettyDataBufferFactory = new NettyDataBufferFactory(ByteBufAllocator.DEFAULT);
//    //    DataBuffer buffer = nettyDataBufferFactory.allocateBuffer(bytes.length);
//    //    buffer.write(bytes);
//    //    return buffer;
//    //}
//    //
//    ///**
//    // * 访问记录对象
//    // */
//    //@Data
//    //private class AccessRecord{
//    //    private String path;
//    //    private String body;
//    //    private MultiValueMap<String,String> queryString;
//    //    private long expendTime;
//    //    private int httpCode;
//    //}
//    //
//    //private void writeAccessLog(String str){
//    //    File file = new File("access.log");
//    //    if (!file.exists()){
//    //        try {
//    //            if (file.createNewFile()){
//    //                file.setWritable(true);
//    //            }
//    //        } catch (IOException e) {
//    //            System.out.println("创建访问日志文件失败.{}"+e.getMessage());
//    //        }
//    //    }
//    //
//    //    try(FileWriter fileWriter = new FileWriter(file.getName(),true)){
//    //        fileWriter.write(str);
//    //    } catch (IOException e) {
//    //        System.out.println("写访问日志到文件失败. {}"+ e.getMessage());
//    //    }
//    //
//    //}
//      /*
//    1.实现GlobalFilter＆Ordered
//    2.重要提示：订单必须小于-1，否则标准NettyWriteResponseFilter将在您的过滤器有机会被调用之前发送响应
//    3.在重写的过滤器方法中，通过exchange.getResponse（）创建一个ServerHttpResponseDecorator，4
//    重写此装饰器的writeWith方法，然后在其中进行主体修改（我必须将主体从Publisher <？扩展为DataBuffer>转换为Flux以​​使其完成。
//    易于修改），返回super.writeWith（yourModifiedBodyFlux）。更改交换并将装饰器设置为新的响应。
//     */
//    /**
//     * 必须要小于 -1
//     */
//    @Override
//    public int getOrder() {
//        return -2;
//    }
//
//    @Override
//    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
//        String value = exchange.getRequest().getPath().value();
//        if("/service-xx/v2/api-docs".equalsIgnoreCase(value)){
//            return chain.filter(exchange);
//        }
//        //开始时间
//        //exchange.getAttributes().put(GatewayConstant.LOG_ATTRIBUTES, LogDTO.builder().startTime(System.currentTimeMillis()).build());
//        //请求方法 只纪录get和post
//        HttpMethod method = exchange.getRequest().getMethod();
//        if( !HttpMethod.GET.matches(method.name()) && !HttpMethod.POST.equals(method)){
//            return chain.filter(exchange);
//        }
//        //请求头 当post时候只纪录json
//        if(HttpMethod.POST.equals(method)){
//            ServerHttpRequest request = exchange.getRequest();
//            HttpHeaders headers = request.getHeaders();
//            if(!headers.containsKey(HttpHeaders.CONTENT_TYPE)){
//                return chain.filter(exchange);
//            }
//            List<String> contextTypes = headers.get(HttpHeaders.CONTENT_TYPE);
//            if(!(contextTypes.contains( MediaType.APPLICATION_JSON_VALUE)
//                    || contextTypes.contains( MediaType.APPLICATION_JSON_UTF8_VALUE))){
//                return chain.filter(exchange);
//            }
//        }
//        /*
//           TODO 忽略下载
//           临时方案从路径中忽略
//           正常应该从相应头中忽略
//           MediaType.APPLICATION_OCTET_STREAM  application/octet-stream
//         */
//        boolean isDownload = Stream.of(
//                exchange.getRequest().getPath().value().toLowerCase().split("/")
//        ).anyMatch("download"::equals);
//        if(isDownload){
//            return chain.filter(exchange);
//        }
//        //参数
//        if( HttpMethod.GET.matches(method.name())){
//            // 记录请求的参数信息 针对GET 请求
//            MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
//            StringBuilder builder = new StringBuilder();
//            for (Map.Entry<String, List<String>> entry : queryParams.entrySet()) {
//                builder.append(entry.getKey()).append("=").append(StringUtils.join(entry.getValue(), ",")).append(",");
//            }
//            LogDTO sysLog = exchange.getAttribute(GatewayConstant.LOG_ATTRIBUTES);
//            sysLog.setParams(builder.toString());
//            return returnMono(chain, exchange);
//        }else {
//            //重新构造request，参考ModifyRequestBodyGatewayFilterFactory
//            ServerRequest serverRequest = ServerRequest.create(exchange, HandlerStrategies.withDefaults().messageReaders());
//            //body
//            Mono<String> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(body -> {
//                LogDTO sysLog = exchange.getAttribute(GatewayConstant.LOG_ATTRIBUTES);
//                sysLog.setParams(body);
//                return Mono.just(body);
//            });
//            BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody, String.class);
//            HttpHeaders headersNew = new HttpHeaders();
//            headersNew.putAll(exchange.getRequest().getHeaders());
//            //猜测这个就是之前报400错误的元凶，之前修改了body但是没有重新写content length
//            // the new content type will be computed by bodyInserter
//            // and then set in the request decorator
//            headersNew.remove(HttpHeaders.CONTENT_LENGTH);
//            // Greenwich版本通过cachedBodyOutputMessage进行构造body请求
//            //MyCachedBodyOutputMessage 这个类完全就是CachedBodyOutputMessage，只不过CachedBodyOutputMessage不是公共的
//            MyCachedBodyOutputMessage outputMessage = new MyCachedBodyOutputMessage(exchange, headersNew);
//            final Mono<Void> then = bodyInserter.insert(
//                    outputMessage,
//                    new BodyInserterContext()
//            ).then(Mono.defer(() -> {
//                ServerHttpRequest decorator = this.decorate(exchange, headersNew, outputMessage);
//                return returnMono(chain, exchange.mutate().request(decorator).build());
//            }));
//            return then;
//        }
//    }
//
//    private Mono<Void> returnMono(GatewayFilterChain chain, ServerWebExchange exchange){
//        return chain.filter(exchange.mutate().response(decorate(exchange)).build()).then(Mono.fromRunnable(()->{
//            //日志
//            ServerHttpRequest request = exchange.getRequest();
//            LogDTO sysLog = exchange.getAttribute(GatewayConstant.LOG_ATTRIBUTES);
//            sysLog.setUri(request.getURI());
//            sysLog.setPath(request.getPath().value());
//            sysLog.setMethod(request.getMethodValue());
//            HttpHeaders headers = request.getHeaders();
//            sysLog.setHeaders(headers);
//            sysLog.setAddress(request.getRemoteAddress());
//            Long startTime = sysLog.getStartTime();
//            long executeTime = (System.currentTimeMillis() - startTime);
//            sysLog.setExecuteTime(executeTime);
//            sysLog.setCode(exchange.getResponse().getStatusCode().value());
//            System.out.println("日志打印:" + JSONObject.toJSONString(sysLog));
//        }));
//    }
//
//    /**
//     * 参考ModifyRequestBodyGatewayFilterFactory
//     */
//    ServerHttpRequestDecorator decorate(ServerWebExchange exchange, HttpHeaders headers,
//                                        MyCachedBodyOutputMessage outputMessage) {
//        return new ServerHttpRequestDecorator(exchange.getRequest()) {
//            @Override
//            public HttpHeaders getHeaders() {
//                long contentLength = headers.getContentLength();
//                HttpHeaders httpHeaders = new HttpHeaders();
//                httpHeaders.putAll(super.getHeaders());
//                if (contentLength > 0) {
//                    httpHeaders.setContentLength(contentLength);
//                }
//                else {
//                    // TODO: this causes a 'HTTP/1.1 411 Length Required' // on
//                    // httpbin.org
//                    httpHeaders.set(HttpHeaders.TRANSFER_ENCODING, "chunked");
//                }
//                return httpHeaders;
//            }
//
//            @Override
//            public Flux<DataBuffer> getBody() {
//                return outputMessage.getBody();
//            }
//        };
//    }
//
//    /**
//     * 参考 ModifyResponseBodyGatewayFilterFactory
//     */
//    @SuppressWarnings("unchecked")
//    ServerHttpResponse decorate(ServerWebExchange exchange) {
//        return new ServerHttpResponseDecorator(exchange.getResponse()) {
//
//            @Override
//            public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
//
//                Class inClass = String.class;
//                Class outClass = String.class;
//
//                String originalResponseContentType = exchange
//                        .getAttribute(ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);
//                HttpHeaders httpHeaders = new HttpHeaders();
//                // explicitly add it in this way instead of
//                // 'httpHeaders.setContentType(originalResponseContentType)'
//                // this will prevent exception in case of using non-standard media
//                // types like "Content-Type: image"
//                httpHeaders.add(HttpHeaders.CONTENT_TYPE,
//                        originalResponseContentType);
//
//                ClientResponse clientResponse = ClientResponse
//                        .create(exchange.getResponse().getStatusCode())
//                        .headers(headers -> headers.putAll(httpHeaders))
//                        .body(Flux.from(body)).build();
//
//                // TODO: flux or mono
//                Mono modifiedBody = clientResponse.bodyToMono(inClass)
//                        .flatMap(originalBody -> {
//                            //此次可以对返回的body进行操作
//                            LogDTO sysLog = exchange.getAttribute(GatewayConstant.LOG_ATTRIBUTES);
//                            sysLog.setResult(originalBody);
//                            return Mono.just(originalBody);
//                        });
//
//                BodyInserter bodyInserter = BodyInserters.fromPublisher(modifiedBody,
//                        outClass);
//                MyCachedBodyOutputMessage outputMessage = new MyCachedBodyOutputMessage(
//                        exchange, exchange.getResponse().getHeaders());
//                return bodyInserter.insert(outputMessage, new BodyInserterContext())
//                        .then(Mono.defer(() -> {
//                            Flux<DataBuffer> messageBody = outputMessage.getBody();
//                            HttpHeaders headers = getDelegate().getHeaders();
//                            if (!headers.containsKey(HttpHeaders.TRANSFER_ENCODING)) {
//                                messageBody = messageBody.doOnNext(data -> headers
//                                        .setContentLength(data.readableByteCount()));
//                            }
//                            // TODO: fail if isStreamingMediaType?
//                            return getDelegate().writeWith(messageBody);
//                        }));
//            }
//
//            @Override
//            public Mono<Void> writeAndFlushWith(
//                    Publisher<? extends Publisher<? extends DataBuffer>> body) {
//                return writeWith(Flux.from(body).flatMapSequential(p -> p));
//            }
//        };
//    }
//}
